﻿Namespace IO
    Public Class BufferedFileReadStream
        Inherits System.IO.Stream

        Private mFilePath As String
        Private mFileSize As Long
        Private mFilePos As Long = 0
        Private mBufferPos As Integer = 0
        Private mBuffer(32 * 1024 - 1) As Byte
        Private mBufferSize As Integer = 0

        Public Sub New(ByVal filePath As String)
            mFilePath = filePath
            Init()
        End Sub
        Public Sub New(ByVal directory As String, ByVal fileName As String)
            mFilePath = System.IO.Path.Combine(directory, fileName)
            Init()
        End Sub
        Private mFileStream As System.IO.FileStream
        Private Sub Init()
            mFileStream = New System.IO.FileStream(mFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Delete)
            mFileSize = mFileStream.Length
            FillBuffer()
        End Sub

        Public Overrides ReadOnly Property CanRead() As Boolean
            Get
                Return True
            End Get
        End Property

        Public Overrides ReadOnly Property CanSeek() As Boolean
            Get
                Return False
            End Get
        End Property

        Public Overrides ReadOnly Property CanWrite() As Boolean
            Get
                Return False
            End Get
        End Property

        Public Overrides Sub Flush()
        End Sub

        Public Overrides ReadOnly Property Length() As Long
            Get
                Return mFileSize
            End Get
        End Property

        Public Overrides Property Position() As Long
            Get
                Return mFilePos
            End Get
            Set(ByVal value As Long)
                Throw New NotSupportedException
            End Set
        End Property

        Public Overrides Function Read(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer) As Integer
            If mFilePos >= mFileSize Then Throw New System.IO.EndOfStreamException
            Dim iLen1 As Integer = CInt(Math.Min(buffer.Count - mBufferPos, mFileSize - mFilePos))
            Dim iOutPos As Integer = offset
            If count > mFileSize - mFilePos Then count = CInt(mFileSize - mFilePos)
            For i As Integer = 0 To iLen1 - 1
                buffer(iOutPos) = mBuffer(mBufferPos)
                mBufferPos += 1
                iOutPos += 1
                mFilePos += 1
            Next
            If iLen1 < count And mFileSize > mFilePos Then
                FillBuffer()
                'we ran out of out buffer, refill it
                For i As Integer = iLen1 To count ' CInt(Math.Min(count, mFileSize - mFilePos))
                    buffer(iOutPos) = mBuffer(mBufferPos)
                    iOutPos += 1
                    mBufferPos += 1
                    mFilePos += 1
                Next
            End If
            Return iOutPos - offset
        End Function

        Private Sub FillBuffer()
            mBufferSize = mFileStream.Read(mBuffer, 0, mBuffer.Count)
            mBufferPos = 0
        End Sub

        Public Overrides Function Seek(ByVal offset As Long, ByVal origin As System.IO.SeekOrigin) As Long
            Throw New NotSupportedException
        End Function

        Public Overrides Sub SetLength(ByVal value As Long)
            Throw New NotSupportedException
        End Sub

        Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As Integer, ByVal count As Integer)
            Throw New NotSupportedException
        End Sub

        Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            MyBase.Dispose(disposing)
            If mFileStream IsNot Nothing Then
                mFileStream.Close()
                mFilePath = Nothing
            End If
        End Sub
    End Class

End Namespace